Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 5x 5x 1x 4x 4x 4x 2x 2x 6x 6x 6x 6x 5x 2x 1x 1x 1x 3x 3x | import { NextRequest, NextResponse } from "next/server";
import { handleUpload, type HandleUploadBody } from "@vercel/blob/client";
import { getSession } from "@/lib/auth";
import { getDb } from "@/lib/db";
import { PhotoController } from "@/server/api/photos";
import { requireApiCapability } from "@/server/dal/collection-access";
import { POOL_ALLOWED_CONTENT_TYPES, POOL_MAX_BYTES } from "@/lib/uploads";
import { errorResponse, HttpError } from "@/server/api/http-errors";
async function assertPhotosWrite(collectionId: string): Promise<string> {
const session = await getSession();
if (!session?.user) {
throw new HttpError("Unauthorized", 401);
}
const db = getDb();
// photos:write = curator — any curator may upload pool photos (full-curator model).
const access = await requireApiCapability(db, collectionId, session.user.id, "photos:write");
if (access instanceof NextResponse) {
throw new HttpError(access.status === 404 ? "Not found" : "Forbidden", access.status);
}
return session.user.id;
}
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
try {
const { id } = await params;
const body = (await req.json()) as HandleUploadBody;
const jsonResponse = await handleUpload({
body,
request: req,
onBeforeGenerateToken: async () => {
// User request: cookies present → authorize and capture the uploader.
const uploadedBy = await assertPhotosWrite(id);
return {
allowedContentTypes: [...POOL_ALLOWED_CONTENT_TYPES],
maximumSizeInBytes: POOL_MAX_BYTES,
addRandomSuffix: true,
tokenPayload: JSON.stringify({ collectionId: id, uploadedBy }),
};
},
onUploadCompleted: async ({ blob, tokenPayload }) => {
// Blob callback: NO session. Trust only the signed tokenPayload.
Iif (!tokenPayload) {
throw new Error("Missing token payload");
}
const { collectionId, uploadedBy } = JSON.parse(tokenPayload) as {
collectionId: string;
uploadedBy: string;
};
await PhotoController.fromDb(getDb()).recordPooled({
collectionId,
uploadedBy,
blobKey: blob.url,
filename: blob.pathname,
mimeType: blob.contentType ?? "application/octet-stream",
fileSize: null,
});
},
});
return NextResponse.json(jsonResponse);
} catch (error) {
return errorResponse(error);
}
}
|